package com.chucang.shucang.common.security.component;

import cn.hutool.extra.spring.SpringUtil;
import com.chucang.shucang.common.security.entity.ShuCangUser;
import com.chucang.shucang.common.security.service.ShuCangUserDetailsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.Ordered;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
import org.springframework.stereotype.Component;

import java.security.Principal;
import java.util.Comparator;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * @author flitsneak
 * @email flitsneak@gmail.com
 * @date 2022/9/24 19:13
 * @description token校验器
 */
@Slf4j
@Component
@RequiredArgsConstructor
@ConditionalOnProperty(name = "oauth2.resource.sever.enabled", havingValue = "true")
public class ShuCangCustomOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
    private final OAuth2AuthorizationService authorizationService;

    @Override
    public OAuth2AuthenticatedPrincipal introspect(String token) {
        OAuth2Authorization oldAuthorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
        if (Objects.isNull(oldAuthorization)) {
            throw new InvalidBearerTokenException(token);
        }

        // 客户端模式默认返回
        if (AuthorizationGrantType.CLIENT_CREDENTIALS.equals(oldAuthorization.getAuthorizationGrantType())) {
            return new ShuCangClientCredentialsOAuth2AuthenticatedPrincipal(oldAuthorization.getAttributes(),
                    AuthorityUtils.NO_AUTHORITIES, oldAuthorization.getPrincipalName());
        }

        Map<String, ShuCangUserDetailsService> userDetailsServiceMap = SpringUtil
                .getBeansOfType(ShuCangUserDetailsService.class);

        Optional<ShuCangUserDetailsService> optional = userDetailsServiceMap.values().stream()
                .filter(service -> service
                        .support(Objects.requireNonNull(oldAuthorization).getRegisteredClientId(), oldAuthorization.getAuthorizationGrantType().getValue()))
                .max(Comparator.comparingInt(Ordered::getOrder));

        UserDetails userDetails = null;
        try {
            Object principal = Objects
                    .requireNonNull(oldAuthorization)
                    .getAttributes()
                    .get(Principal.class.getName());
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = (UsernamePasswordAuthenticationToken) principal;
            Object tokenPrincipal = usernamePasswordAuthenticationToken.getPrincipal();
            userDetails = optional
                    .get()
                    .loadUserByUser((ShuCangUser) tokenPrincipal);
        } catch (UsernameNotFoundException notFoundException) {
            log.warn("用户不不存在 {}", notFoundException.getLocalizedMessage());
            throw notFoundException;
        } catch (Exception ex) {
            log.error("资源服务器 introspect Token error {}", ex.getLocalizedMessage());
        }
        return (ShuCangUser) userDetails;
    }
}
